#include "Symbolyzer.h"
#include "Keywords.h"
#include "StringOps.h"
#include "StringOps.h"
#include "Method.h"

#include<iostream>
#include<string>
#include<set>
 
using namespace std;

Symbolyzer::Symbolyzer()
{
	m_eMode = eSymbolyzerInitial;
	m_eModeHistory = eSymbolyzerInitial;
	
	m_pxLookaheadScope = NULL;

	m_pxImmediateScope = NULL;
	m_sLastSymbol = "";

	m_bInsideFunction = false;
	m_bFuncDefActive = false;
}

Symbolyzer::~Symbolyzer(void)
{
}

void Symbolyzer::onNewToken(Scope& inScope, Token& inToken)
{
    char cDelimiter=char(inToken);
	switch(cDelimiter)
	{
	case '\n':
	case ' ':
	case '\t':
	case '\r':
		//TODO: Replace with iswhite
		return;
		break;

	case';':
		break;
	}

	if(!m_pxCurrentScope->isCode())
		return;

	if(m_bFuncDefActive)
	{
		char cDelimiter = char(inToken);
		if(cDelimiter == ';') 
			m_bFuncDefActive = false;
	}

	//Datalyzer part
	Scope::Type eScopeType = m_pxCurrentScope->getScopeType();

	//Assuming that variables can be defined only in these scopes
	//Exception is parameter-list, handled in gatherParameters
	if( (eScopeType!= Scope::eScopeBlock) &&
		(eScopeType!= Scope::eScopeGlobal) &&
		 (eScopeType!=Scope::eScopeParen)
		 )
		return;

	if((!inToken.m_bIsDelimiter)
		&&(!inToken.m_bIsKeyword))
	{
		m_sLastSymbol = inToken.m_sValue;
	}

	if(m_eMode == eSymbolyzerInitial)
	{
		if(Keywords::isDataTypeDefiner(inToken.m_sValue))
		{
			if(inToken.m_sValue == "typedef") //Special case
				m_eMode = eSymbolyzerTypedef;
			else
				m_eMode = eSymbolyzerDataType;

			return;
		}
		else
		{
			if(isDataType(inToken.m_sValue))
				m_eMode = eSymbolyzerVariable;
		}
		
	}

	switch(m_eMode)
	{
	case eSymbolyzerDataType:
		matchDataType(inToken);
		break;

	case eSymbolyzerVariable:
		matchVariable(inToken);
		break;

	case eSymbolyzerTypedef:
		matchTypedef(inToken);
		break;
	}

}

void Symbolyzer::onScopeEntry(Scope& inNewScope, Scope& inOldScope)
{
    m_pxCurrentScope = &inNewScope;
	m_eModeHistory = m_eMode;

	m_eMode = Symbolyzer::eSymbolyzerInitial;
	m_sLastSymbol = "";

    Scope::Type eNewScope = inNewScope.m_eType;

    switch(eNewScope)
    {
    case Scope::eScopeBlock:
		//FIXME: Hack, should be automatic from scuba
		inOldScope.m_asElement.push_back("");
		if(m_pxCurrentScope->isFunction())
			m_bInsideFunction = true;
        onEntryBlock(inNewScope, inOldScope);
        break;

    case Scope::eScopeParen:
        onEntryParen(inNewScope, inOldScope);
        break;

    case Scope::eScopeCommentC:
    case Scope::eScopeCommentCPP:
        break;

    default:
        break;
    }
}

void Symbolyzer::onScopeExit(Scope& inNewScope, Scope& inOldScope)
{
    m_pxCurrentScope = &inNewScope;
	m_eMode = m_eModeHistory;
	m_sLastSymbol = "";

	bool		bInNamespaceNow	= inNewScope.canDefineFunction();
	bool		bWasInFunction	= inOldScope.isFunction(); 

	if(bWasInFunction)
		m_bInsideFunction = false;

	if(bInNamespaceNow)
	{
		if(bWasInFunction)
			return;

		//Store lookahead for function which may be defined
		if(inOldScope.m_eType == Scope::eScopeParen)
		{
			m_bFuncDefActive = true;
			m_pxLookaheadScope = &inOldScope;
			return;
		}
	}
}

void Symbolyzer::onEntryParen(Scope& inNewScope, Scope& inOldScope)
{
    bool bWasInNamespace = inOldScope.canDefineFunction();

    if(bWasInNamespace)
    {
		std::string sMethod = inOldScope.m_asElement.back();
		std::string sMethodType;
		std::string sMethodName;

        splitAsNameAndType(sMethod,sMethodName,sMethodType);
		
		Symbol xSymbol;
		xSymbol.m_sName = sMethodName;
		xSymbol.m_sTypePrefix = sMethodType;
		xSymbol.m_xDefinition = *m_pxLocation;
		xSymbol.m_eSymbolType = Symbol::eSymbolFunction;

		defineSymbol(xSymbol, &inOldScope);
    }
}

void Symbolyzer::onEntryBlock(Scope& inNewScope, Scope& inOldScope)
{
    bool bWasInNamespace = inOldScope.canDefineFunction();

	//Making parameter list
	if(bWasInNamespace)
    {
		if(m_pxLookaheadScope!=NULL)
		{
			inNewScope.m_apxRelative.push_back(m_pxLookaheadScope);
			m_pxLookaheadScope = NULL;
		}
    }
	//TODO: Inheritance scope also to be done like this. LookaheadScope will need to be stack if multiple inheritance :)
	//Not scoped for 0.10
}

void Symbolyzer::matchDataType(Token& inToken)
{

	if(isDataType(inToken.m_sValue))
	{
		//if it is already present, then it is for a variable definition
		m_eMode = eSymbolyzerVariable;
	}
	else
	{
		//it has to be a new data-type name. That simple!
		Symbol xSymbol;
		xSymbol.m_sName = inToken.m_sValue;
		xSymbol.m_eSymbolType = Symbol::eSymbolDataType;
		xSymbol.m_xDefinition = *m_pxLocation;

		defineSymbol(xSymbol, m_pxCurrentScope);

		m_eMode = eSymbolyzerVariable;
	}
}

void Symbolyzer::matchTypedef(Token &inToken)
{
	char cDelimiter = char(inToken);
	if(cDelimiter != ';')
		return;

	std::string sTypeDefiner = m_pxCurrentScope->m_asElement.back();
	std::string sTypeDefinerType;
	std::string sTypeDefinerName;

	//TODO: Handle :, etc
	splitAsNameAndType(sTypeDefiner,sTypeDefinerName,sTypeDefinerType);

	Symbol xSymbol;
	xSymbol.m_sName = sTypeDefinerName;
	xSymbol.m_eSymbolType = Symbol::eSymbolDataType;
	xSymbol.m_xDefinition = *m_pxLocation;
	defineSymbol(xSymbol,m_pxCurrentScope);

	m_eMode = eSymbolyzerInitial;
}

void Symbolyzer::matchVariable(Token& inToken)
{
	if(!inToken.m_bIsDelimiter)
		return;

	char cDelimiter = char(inToken);

	switch(cDelimiter)
	{
	case ';':
	case '=':
	case ',':
	case ')':
		break;

	default:
		return;
		break;
	}

	std::string sVariable = m_pxCurrentScope->m_asElement.back();
	if(sVariable=="")
	{
		//It was just a data-type definition
		m_eMode = eSymbolyzerInitial;
		return;
	}

	Symbol xSymbol;
	std::string sVariableType;
	std::string sVariableName;

	sVariable = m_pxCurrentScope->m_asElement.back();
	splitAsNameAndType(sVariable,sVariableName,sVariableType);

	if(m_pxCurrentScope->canDefineVariable())
	{
		xSymbol.m_sName = sVariableName;
		xSymbol.m_sTypePrefix = sVariableType;
		xSymbol.m_eSymbolType = Symbol::eSymbolVariable;
		xSymbol.m_xDefinition = *m_pxLocation;

		if(!isDataType(sVariableName)) //Blocking typedefs
			defineSymbol(xSymbol,m_pxCurrentScope);
	}
	else
	{
		xSymbol.m_sName = sVariableName;
		xSymbol.m_sTypePrefix = sVariableType;
		xSymbol.m_eSymbolType = Symbol::eSymbolAttribute;
		xSymbol.m_xDefinition = *m_pxLocation;
		defineSymbol(xSymbol,m_pxCurrentScope);
	}

	if(cDelimiter == ',')
	{
		if(m_pxCurrentScope->getScopeType() == Scope::eScopeParen)	
		{
			m_pxCurrentScope->m_asElement.push_back("");
			m_eMode = eSymbolyzerInitial;
			return;
		}
		
		//Else next time same type has to be available
		sVariable = m_pxCurrentScope->m_asElement.back();
		splitAsNameAndType(sVariable,sVariableName,sVariableType);
		sVariableType +=" ";
		m_pxCurrentScope->m_asElement.push_back(sVariableType);

		//Continue in same mode
	}
	else
		m_eMode = eSymbolyzerInitial;
}

bool Symbolyzer::isDataType(std::string& s)
{
	if(Keywords::isDataType(s))
		return true;

	Symbol xSymbol;
	checkSymbol(s,xSymbol);
	if(xSymbol.m_eSymbolType == Symbol::eSymbolDataType)
		return true;

	return false;
}

void Symbolyzer::checkSymbol(string s, Symbol& in_xSymbol)
{
	m_pxCurrentScope->findSymbol(s,in_xSymbol);
}

void Symbolyzer::defineSymbol(Symbol in_xSymbol, Scope* in_pxScope)
{
	in_pxScope->m_pxSymbolTable->addSymbol(in_xSymbol);
}








